package org.deltaset.meta.internal;

import org.deltaset.meta.CollectionHandler;
import org.deltaset.meta.MetaHandlerFactory;
import org.deltaset.meta.Catalog;
import org.deltaset.meta.EntityResolver;
import org.deltaset.meta.LabelResolver;
import org.deltaset.meta.MetaAction;
import org.deltaset.meta.MetaActionExecutor;
import org.deltaset.meta.MetaActionHandler;
import org.deltaset.meta.MetaEntry;
import org.deltaset.meta.OptionBuilder;
import org.deltaset.meta.aware.ExecuteListenerProviderAware;
import org.deltaset.meta.aware.MetaHandlerProviderAware;
import org.deltaset.meta.aware.RepositoryProviderAware;
import org.deltaset.meta.aware.TitleResolverProviderAware;
import org.deltaset.meta.extension.CatalogProvider;
import org.deltaset.meta.extension.ExecuteListenerProvider;
import org.deltaset.meta.extension.LabelResolverProvider;
import org.deltaset.meta.extension.MetaActionExecutorProvider;
import org.deltaset.meta.extension.MetaHandlerProvider;
import org.deltaset.meta.extension.OptionBuilderProvider;
import org.deltaset.meta.extension.ProviderDecorator;
import org.deltaset.meta.extension.RepositoryProvider;
import org.deltaset.meta.extension.TitleResolverProvider;

/**
 *
 * @author dmakariev
 */
public final class DefaultMetaHandlerFactory implements MetaHandlerFactory {

    private final CatalogProvider catalogProvider;
    private final MetaHandlerProvider metaHandlerProvider;
    private final LabelResolverProvider labelResolverProvider;
    private final MetaActionExecutorProvider metaActionExecutorProvider;
    private final OptionBuilderProvider optionBuilderProvider;
    private final RepositoryProvider repositoryProvider;
    private final TitleResolverProvider titleResolverProvider;
    private final ExecuteListenerProvider executeListenerProvider;
    private final CollectionHandler collectionHandler;

    public DefaultMetaHandlerFactory(
            final CatalogProvider catalogProvider,
            final MetaHandlerProvider metaHandlerProvider,
            final LabelResolverProvider labelResolverProvider,
            final MetaActionExecutorProvider metaActionExecutorProvider,
            final OptionBuilderProvider optionBuilderProvider,
            final RepositoryProvider repositoryProvider,
            final TitleResolverProvider titleResolverProvider,
            final ExecuteListenerProvider executeListenerProvider) {
        this.catalogProvider = catalogProvider;
        this.metaHandlerProvider = metaHandlerProvider;
        this.labelResolverProvider = labelResolverProvider;
        this.metaActionExecutorProvider = metaActionExecutorProvider;
        this.optionBuilderProvider = optionBuilderProvider;
        this.repositoryProvider = repositoryProvider;
        this.titleResolverProvider = titleResolverProvider;
        this.executeListenerProvider = executeListenerProvider;
        //////
        this.collectionHandler = new CollectionHandlerImpl();

        processAwareForProviders();
    }

    private void processAwareForProviders() {
        processProvider(this.metaHandlerProvider);
        processProvider(this.optionBuilderProvider);
    }

//    @Override
//    public MetaHandler createMetaHandler(final String entityClassName) {
//        final Class loadedClass = loadClass(entityClassName);
//        return createMetaHandler(loadedClass);
//    }
//
//    @Override
//    public MetaHandler createMetaHandler(final Class entityClass) {
//        final MetaHandler metaHandler = metaHandlerProvider.createMetaHandler(entityClass);
//        return metaHandler;
//    }
    
    @Override
    public MetaActionHandler createMetaActionHandler(){
        final MetaActionHandler actionHandler = metaHandlerProvider.createMetaActionHandler();
        return actionHandler;
    }

    @Override
    public MetaEntry createMetaEntry(final Class entityClass){
        final MetaEntry metaEntry = metaHandlerProvider.createMetaEntry(entityClass);
        return metaEntry;
    }

    @Override
    public MetaActionExecutor createMetaActionExecutor(final MetaAction metaAction) {
        return metaActionExecutorProvider.createMetaActionExecutor(metaAction);
    }

    @Override
    public Catalog createCatalog(final String extensionCatalogName) {
        return catalogProvider.createCatalog(extensionCatalogName);
    }

    @Override
    public LabelResolver createLabelResolver() {
        return labelResolverProvider.createLabelResolver();
    }

    @Override
    public EntityResolver createEntityResolver() {
        return repositoryProvider.createEntityResolver();
    }

    @Override
    public <T> OptionBuilder<T> createOptionBuilder() {
        final OptionBuilder<T> optionBuilder = optionBuilderProvider.createOptionBuilder();
        return optionBuilder;
    }
    
    @Override
    public CollectionHandler createCollectionHandler() {
        return collectionHandler;
    }

///////////////
    /**
     * checks for Aware interfaces, on providers  (,or if needed on result objects? )
     * used only where needed, could be applied on all if needed 
     */
    private <T> T processProvider(T provider) {
        return processDecoratedProvider(provider);
    }

    private <T> T processDecoratedProvider(T provider) {
        if (provider instanceof ProviderDecorator) {
            final ProviderDecorator<T> decoratorProvider = (ProviderDecorator<T>) provider;
            final T defaultProvider = decoratorProvider.getDefaultProvider();
            final T awaredProvider = processAware(defaultProvider);
            decoratorProvider.setDefaultProvider(awaredProvider);
            final T processedProvider = (T) decoratorProvider;
            return processedProvider;
        } else {
            return processAware(provider);
        }
    }

    private <T> T processAware(T provider) {

        if (provider instanceof MetaHandlerProviderAware) {
            ((MetaHandlerProviderAware) provider).setMetaHandlerProvider(metaHandlerProvider);
        }

        if (provider instanceof TitleResolverProviderAware) {
            ((TitleResolverProviderAware) provider).setTitleResolverProvider(titleResolverProvider);
        }

        if (provider instanceof RepositoryProviderAware) {
            ((RepositoryProviderAware) provider).setRepositoryProvider(repositoryProvider);
        }

        if (provider instanceof ExecuteListenerProviderAware) {
            ((ExecuteListenerProviderAware) provider).setExecuteListenerProvider(executeListenerProvider);
        }

        return provider;
    }

    private static Class loadClass(final String className) {
        try {
            final Class loadedClass = Class.forName(className);
            return loadedClass;
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException("Cannot load class:" + className, ex);
        }
    }   
}
